﻿// <copyright file="UserRectangleView.cs" company="Ecole Normale Supérieure">
// Released under GPLv2
// </copyright>
// <summary>Contains the UserView class.</summary>

namespace Sqwarea.GameLogic.View
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using Sqwarea.Persistence;

    /// <summary>
    /// Class containing a view of a rectangle of squares.
    /// Used by GUI.
    /// </summary>
    public class UserRectangleView
    {
        /// <summary>
        /// Array containing real squares of the view
        /// </summary>
        private Square[,] _squares;

        /// <summary>
        /// Kings cache used by SquareView
        /// </summary>
        private IKingsCache _kingsCache;

        /// <summary>
        /// Initializes a new instance of the UserRectangleView class.
        /// </summary>
        /// <param name="kingId">Id of the user requesting this view.</param>
        /// <param name="squares">Array of real squares of this view.
        /// Should be (rectangle width + 2)*(rectangle height + 2).
        /// Contains 1 square border.</param>
        /// <param name="rectangle">Rectangle containing this view.</param>
        /// <param name="kingsCache">The Game Provider</param>
        public UserRectangleView(long kingId, Square[,] squares, Rectangle rectangle, IKingsCache kingsCache)
        {
            if (rectangle.Size.Height > int.MaxValue - 2 ||
                squares.GetLength(0) != rectangle.Size.Height + 2)
            {
                throw new ArgumentException("UserView : squares array height != rectangle height + 2");
            }

            if (rectangle.Size.Width > int.MaxValue - 2 ||
                squares.GetLength(1) != rectangle.Size.Width + 2)
            {
                throw new ArgumentException("UserView : squares array width != rectangle width + 2");
            }

            if (null == kingsCache)
            {
                throw new ArgumentNullException("kingsCache", "UserRectangleView");
            }

            this.KingId = kingId;
            this._squares = squares;
            this.Rectangle = rectangle;
            this._kingsCache = kingsCache;
        }

        /// <summary>
        /// Gets the rectangle related to the view.
        /// </summary>
        public Rectangle Rectangle { get; private set; }

        /// <summary>
        /// Gets the king id who requested the view.
        /// </summary>
        public long KingId { get; private set; }

        /// <summary>
        /// Gets all squares view in rectangle.
        /// </summary>
        /// <returns>The bi-dimentional array containing the squares</returns>
        public SquareView[,] GetSquares()
        {
            var result = new SquareView[this.Rectangle.Size.Height, this.Rectangle.Size.Width];

            for (int i = 0; i < this.Rectangle.Size.Height; i++)
            {
                for (int j = 0; j < this.Rectangle.Size.Width; j++)
                {
                    if (!this.IsNeighborSquare(i, j))
                    {
                        result[i, j] = new SquareView(this._squares[i + 1, j + 1], false, false, _kingsCache);
                    }
                    else
                    {
                        result[i, j] = new SquareView(this._squares[i + 1, j + 1], true, true, _kingsCache);
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Gets all modified squares view in rectangle.
        /// </summary>
        /// <param name="squaresETags">The ETags of the previous squares (used to say if square is modified or not)</param>
        /// <returns>The list of modified squares</returns>
        public IList<SquareView> GetSquares(Dictionary<Point, string> squaresETags)
        {
            var result = new HashSet<SquareView>();

            for (int i = 0; i < this.Rectangle.Size.Height; i++)
            {
                for (int j = 0; j < this.Rectangle.Size.Width; j++)
                {
                    var square = this._squares[i + 1, j + 1];

                    if (null == square.ETag ||
                        !squaresETags.ContainsKey(square.Position) ||
                        squaresETags[square.Position] != square.ETag)
                    {
                        var neighbor = IsNeighborSquare(i, j);
                        result.Add(new SquareView(square, neighbor, neighbor, _kingsCache));

                        // We force to update neighbours when we win a square
                        if (square.KingId == this.KingId)
                        {
                            foreach (var s in GetNeighbours(i, j))
                            {
                                result.Add(new SquareView(s, true, true, _kingsCache));
                            }
                        }

                        squaresETags[square.Position] = square.ETag;
                    }
                    else if (square.HasKing)
                    {
                        var neighbor = IsNeighborSquare(i, j);
                        if (neighbor)
                        {
                            result.Add(new SquareView(square, neighbor, neighbor, _kingsCache));
                        }
                    }
                }
            }

            return result.ToList();
        }

        /// <summary>
        /// Gets the SquareView at the relative position (line,column) in the view (not the entire map).
        /// </summary>
        /// <param name="line">Line selected (0 is the first line of the rectangle of the view)</param>
        /// <param name="column">Column selected (0 is the first column of the rectangle of the view)</param>
        /// <remarks>Notice that the requested square is _squares[line + 1, column + 1].</remarks>
        /// <returns>See summary</returns>
        public SquareView GetRelativeSquare(int line, int column)
        {
            if (line > int.MaxValue - 2 ||
                line < int.MinValue + 2 ||
                line >= this.Rectangle.Size.Height)
            {
                throw new ArgumentOutOfRangeException("line");
            }

            if (column > int.MaxValue - 2 ||
                column < int.MinValue + 2 ||
                column >= this.Rectangle.Size.Width)
            {
                throw new ArgumentOutOfRangeException("column");
            }

            if (!this.IsNeighborSquare(line, column))
            {
                return new SquareView(this._squares[line + 1, column + 1], false, false, _kingsCache);
            }
            else
            {
                return new SquareView(this._squares[line + 1, column + 1], true, true, _kingsCache);
            }
        }

        /// <summary>
        /// Return the neighbours in the view.
        /// WARNING : There is no verification for arguments range.
        /// </summary>
        /// <param name="line">Line selected (0 is the first line of the rectangle of the view)</param>
        /// <param name="column">Column selected (0 is the first column of the rectangle of the view)</param>
        /// <returns>See summary</returns>
        internal IEnumerable<Square> GetNeighbours(int line, int column)
        {
            if (line <= Rectangle.Size.Height - 2)
            {
                yield return this._squares[line + 2, column + 1];
            }

            if (line >= 1)
            {
                yield return this._squares[line, column + 1];
            }

            if (column <= Rectangle.Size.Width - 2)
            {
                yield return this._squares[line + 1, column + 2];
            }

            if (column >= 1)
            {
                yield return this._squares[line + 1, column];
            }
        }

        /// <summary>
        /// Return true if and only if the square at the relative position (line,column) is a square of the kingdom or a neightbor square.
        /// WARNING : There is no verification for arguments range.
        /// </summary>
        /// <param name="line">Line selected (0 is the first line of the rectangle of the view)</param>
        /// <param name="column">Column selected (0 is the first column of the rectangle of the view)</param>
        /// <returns>See summary</returns>
        private bool IsNeighborSquare(int line, int column)
        {
            return !(this._squares[line + 1, column + 1].KingId != this.KingId &&
                this._squares[line + 1, column + 2].KingId != this.KingId &&
                this._squares[line + 1, column + 0].KingId != this.KingId &&
                this._squares[line + 2, column + 1].KingId != this.KingId &&
                this._squares[line + 0, column + 1].KingId != this.KingId);
        }
    }
}
